<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript" src="lib/jquery-1.11.0.min.js"></script>
</head>
<body>


    <script>

        (function(){
            //3.3.3

            /*
            二进制转换十进制公式
            abcd.efg(2)=d*2^0+c*2^1+b*2^2+a*2^3 + e*2^-1+f*2^-2+g*2^-3(10)

            十进制小数转二进制
            如：0.625=（0.101）B
            0.625*2=1.25======取出整数部分1
            0.25*2=0.5========取出整数部分0
            0.5*2=1==========取出整数部分1
            JS的小数点精确到第16位
            */
            //isNaN() 是否不是一个数值

            //3.6.8
            //with 语句的作用是将代码的作用域设置到一个特定的对象中
            var bb = Number(1.25).toFixed(2);
            console.log(typeof bb);
        });

        (function(){
            //4.1.4==================================

            //按共享传递(obj是实参引用的副本)
            function setName(obj) {
                obj.name = "Nicholas";
                obj = new Object();
                obj.name = "Greg";
            }
            var person = new Object();
            setName(person);
            //alert(person.name); //"Nicholas"

            //var asc = {name:1};
            //var bsc = a;
            //bsc = {name:2};
            //console.log(b);
            //console.log(a);


            //4.1.5===================================
            //确定一个值是哪种基本类型可以使用typeof 操作符，而确定一个值是哪种引用类型可以使用instanceof 操作符。
            //Array.isArray([])
            //alert(person instanceof Object); // 变量 person 是 Object 吗？
            //alert(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗？
            //alert([] instanceof Array)
            //typeof 用于判断基本类型和function
            //instanceof  用于单一环境下 对象类型（如果用基本类型调用此方法 始终返回false）
　　　       //isArray 可以跨环境判断

            //4.2.1====================================
            //延长作用域链 for if with都会存在
            function buildUrl() {
                var qs = "?debug=true";
                with(location){
                    var url = href + qs;
                }
                return url;
            }

            /*Js 变量可以用来保存两种类型的值：基本类型值和引用类型值。
            Undefined、 Null、 Boolean、 Number 和 String。
            基本类型值和引用类型值具有以下特点：
            基本类型值在内存中占据固定大小的空间，因此被保存在栈内存中；
            从一个变量向另一个变量复制基本类型的值，会创建这个值的一个副本；
            引用类型的值是对象，保存在堆内存中；
            包含引用类型值的变量实际上包含的并不是对象本身，而是一个指向该对象的指针；
            从一个变量向另一个变量复制引用类型的值，复制的其实是指针，因此两个变量最终都指向同
            一个对象*/
        });

        (function(){
            //5.0.0 对象是某个引用类型的实例

            //5.0.1 通常，除非必须使用变量来访问属性，否则我们建议使用点表示法[变量名]

            //5.2.8 Array迭代方法====================================

            //every()：对数组中的每一项运行给定函数，如果该函数对每一项都返回true，则返回true。
            //filter()：对数组中的每一项运行给定函数，返回该函数会返回true 的项组成的数组。
            //forEach()：对数组中的每一项运行给定函数。这个方法没有返回值。
            //map()：对数组中的每一项运行给定函数，返回每次函数调用的结果组成的数组。
            //some()：对数组中的每一项运行给定函数，如果该函数对任一项返回true，则返回true。

            //5.2.9 归并====================================
            //前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项
            /*var values = [1,2,3,4,5];
            var sum = values.reduce(function(prev, cur, index, array){
                return prev + cur;
            });*/

        })();

        (function(){

            //5.5 Function==================================
            //指向同一个地址
            /*function sum(num1, num2){
                return num1 + num2;
            }

            alert(sum(10,10)); //20

            var anotherSum = sum;

            alert(anotherSum(10,10)); //20

            sum = null;

            alert(anotherSum(10,10));*/ //20

            /*5.5.4
              函数的名字仅仅是一个包含指针的变量而已。因此，即使是在不同的环境中执行，全局的
              sayColor()函数与 o.sayColor()指向的仍然是同一
              个函数。
            */

        });

        (function(){

            //5.5.5强大的地方是能够扩充函数赖以运行的作用域
            window.color = "red";
            var o = { color: "blue" };

            function sayColor(){
                alert(this.color);
            }
            sayColor();                //red
            sayColor.call(this);       //red
            sayColor.call(window);     //red
            sayColor.call(o);          //blue


            //这个方法会创建一个函数的实例，其 this 值会被绑定到传给 bind()函数的值
            (function(){
                var color = "red";
                var o = { color: "blue" };
                function sayColor(){
                    alert(this.color);
                }
                var objectSayColor = sayColor.bind(o);
                objectSayColor(); //blue
                sayColor(); //red
            })
        });

        (function(){
            var s = 'abcde';
            //s.charAt(1); 求字符
            //s.indexOf('e') 求索引
            //lastIndexOf 从末尾向前搜索 计数还是从头开始

            //var stringValue = "hello world"
            //stringValue.toLocaleUpperCase
            //stringValue.toLocaleLowerCase

            //5.5.7.2
            function selectFrom(lowerValue, upperValue) {
                var choices = upperValue - lowerValue + 1;
                return Math.floor(Math.random() * choices + lowerValue);
            }
            var num = selectFrom(2, 12);
            console.log(num); // 介于 2 和 10 之间（包括 2 和 10）的一个数值
        });

        //面向对象******************************************
        (function(){
            //属性类型:

            // [[Configurable]] 表示能否通过delete删除属性从而重新定义属性，能否修改属性的特性，或者能否把属性修改为访问器属性.
            // [[Enumerable]] 表示能否通过 for-in 循环返回属性
            // [[Writable]] 表示能否修改属性的值.
            // [[Value]] 包含这个属性的数据值.
            //_year 前面的下划线是一种常用的记号，用于表示只能通过对象方法访问的属性
            //6.1.1
            var a = {
                name:'jack'
            }

            //数据属性
            Object.defineProperty(a,'name',{
                //writable:true,
                //value:'hahaha',
            })

            //访问器属性
            Object.defineProperty(a,'api',{
                get: function(){
                    return this.name;
                },

                set:function(newValue){
                    if(newValue == 1){
                        this.name = 'jackchen';
                    }
                }
            })

            a.api = 2;
            alert(a.api)

            //定义数据属性和访问器属性
            //Object.defineProperties(book, {})

            //判断是访问器属性还是数据属性
            //Object.getOwnPropertyDescriptor(book, "_year")

        });



        (function(){

            //工厂模式
            function createPerson(name, age, job){
                var o = new Object();
                o.name = name;
                o.age = age;
                o.job = job;
                o.sayName = function(){
                    alert(this.name);
                };
                return o;
            }
            var person1 = createPerson("Nicholas", 29, "Software Engineer");
            console.log(person1.constructor)
        });

        (function(){

            function createPerson(name, age){
                this.name = name;
                this.age = age;
            }
            createPerson.prototype.sex = 'boy';

            var person1 = new createPerson('lisa',12);

            //获取对象的原型
            //alert(Object.getPrototypeOf(person1) == createPerson.prototype)
            //alert(Object.getPrototypeOf(person1).sex); //"Nicholas"


            //使用hasOwnProperty()方法可以检测一个属性是存在于实例中，还是存在于原型中。
            //alert(person1.hasOwnProperty('sex'))
            //alert(person1.hasOwnProperty("name"));  //false
            //alert("name" in person1);  //true

            //获取原型链属性
            function hasPrototypeProperty(object, name){
                return !object.hasOwnProperty(name) && (name in object);
            }


        });

        (function(){

            //Object.keys()方法。这个方法接收一个对象作为参数，返回一个包含所有可枚举属性的字符串数组
            function Person(){}
            Person.prototype.name = "Nicholas";
            Person.prototype.age = 29;
            Person.prototype.sayName = function(){
                alert(this.name);
            };

            //var keys = Object.keys(Person.prototype);
            //alert(keys);       //"name,age,sayName"

            //如果你想要得到所有实例属性，无论它是否可枚举，都可以使用 Object.getOwnPropertyNames()方法
            //var keys = Object.getOwnPropertyNames(Person.prototype);
            //alert(keys); //"constructor,name,age,job,sayName"

            var p1 = new Person();
            p1.name = "Rob";
            p1.age = 31;
            var p1keys = Object.keys(p1);
            alert(p1keys);    //"name,age"
        });

        (function(){

            //重写constructor
            function Person(){
            }
            Person.prototype = {
                constructor : Person,
                name : "Nicholas",
                age : 29,
                job: "Software Engineer",
                sayName : function () {
                    alert(this.name);
                }
            };

            //实例中的指针仅指向原型，而不指向构造函数。
            var friend = new Person();
            Person.prototype = {
                constructor: Person,
                name : "Nicholas",
                age : 29,
                job : "Software Engineer",
                sayName : function () {
                    alert(this.name);
                }
            };
            friend.sayName();   //error

        });

        //6.2.4组合模式示例
        (function(){
            function Person(name, age, job){
                this.name = name;
                this.age = age;
                this.job = job;
                this.friends = ["Shelby", "Court"];
            }

            Person.prototype = {
                constructor : Person,
                sayName : function(){
                    alert(this.name);
                }
            }

            var person1 = new Person("Nicholas", 29, "Software Engineer");
            var person2 = new Person("Greg", 27, "Doctor");

            person1.friends.push("Van");
            alert(person1.friends);    //"Shelby,Count,Van"
            alert(person2.friends);    //"Shelby,Count"
            alert(person1.friends === person2.friends);    //false
            alert(person1.sayName === person2.sayName);    //true


            var res = (function() {
                var num = 1;
                setTimeout(function(){
                    console.log(num)
                },1000)
                return function(){
                    console.log(num);
                    num++;
                }
            });
        });

        //动态原型模式
        (function(){

            function Person(name, age, job){
                //属性
                this.name = name;
                this.age = age;
                this.job = job;
                //方法
                /*if (typeof this.sayName != "function"){
                    Person.prototype.sayName = function(){
                        alert(this.name);
                    };
                }*/
            }
            Person.prototype.sayName = function(){
                alert(this.name);
            };
            var friend = new Person("Nicholas", 29, "Software Engineer");
            var friend2 = new Person("Nicholas", 29, "Software Engineer");
            console.log(friend.sayName == null);
            console.log(friend2.sayName())
            //friend.sayName();

            //寄生函数构造模式
            //稳妥性模式防止外部访问内部引用(不适用this new)
            function Person2(name, age, job){
                var o = new Object();
                o.name = name;
                o.age = age;
                o.job = job;
                o.sayName = function(){
                    alert(this.name);
                };
                return o;
            }
            //var friend2 = Person2("Nicholas", 29, "Software Engineer");
            //console.log(friend2.__proto__)
            //friend2.sayName(); //"Nicholas"
        })();


        //6.3.2借用构造函数模式[继承]
        (function(){

            function SuperType(){
                this.colors = ["red", "blue", "green"];
            }
            SuperType.prototype.age = 11;

            function SubType(){
                //继承了 SuperType
                SuperType.call(this);
            }
            var instance1 = new SubType();
            instance1.colors.push("black");
            alert(instance1.age)
            alert(instance1.colors);    //"red,blue,green,black"
            //var instance2 = new SubType();
            //alert(instance2.colors);    //"red,blue,green"
        });


        (function(){
            //6.3.3组合继承即可以在子类型构造函数中向超类型数传递参数。
            function SuperType(name){
                this.name = name;
                this.colors = ["red", "blue", "green"];
            }

            SuperType.prototype.sayName = function() {
                alert(this.name);
            }

            function SubType(name, age){
                SuperType.call(this, name);
                this.age = age;
            }

            //继承方法1
            SubType.prototype = new SuperType();
            SubType.prototype.constructor = SubType;
            SubType.prototype.sayAge = function(){
                alert(this.age);
            };

            //继承方法2
            //inheritPrototype(subType, superType); 调用6.3.4

            var instance1 = new SubType("Nicholas", 29);
            instance1.colors.push("black");
            alert(instance1.colors);      //"red,blue,green,black"
            //instance1.sayName();          //"Nicholas";
            instance1.sayAge();           //29

            var instance2 = new SubType("Greg", 27);
            alert(instance2.colors);      //"red,blue,green"
            //instance2.sayName();          //"Greg";
            //instance2.sayAge();           //27


        });

        //6.3.4继承实例对象 浅拷贝
        (function(){
            //浅拷贝一个对象的函数(主要使用new)
            function object(o){
                function F(){}
                F.prototype = o;
                return new F();
            }

            function inheritPrototype(subType, superType){
                var prototype = object(superType.prototype); //创建对象 or
                //var prototype = Object.create(superType.prototype);
                prototype.constructor = subType; //增强对象
                subType.prototype = prototype; //指定对象
            }

        });

        (function(){
            var person = {
                name: "Nicholas1",
                friends: ["Shelby", "Court", "Van"]
            };
            var anotherPerson = Object.create(person);
            anotherPerson.name = "Greg";
            anotherPerson.friends.push("Rob");

            var yetAnotherPerson = Object.create(person);
            yetAnotherPerson.name = "Linda";
            yetAnotherPerson.friends.push("Barbie");

            alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
            //何属性都会覆盖原型对象上的同名属性。
            /*var anotherPerson = Object.create(person, {
                name: {
                    value: "Greg"
                }
            });*/
        });


        //7.1
        (function(){

            var factorial = (function f(num){
                if (num <= 1){
                    return 1;
                } else {
                    return num + f(num-1);
                }
            });
            console.log(factorial(5));
        });



    </script>

</body>
</html>